/** @file
  HII Demo Application - demonstrates various HII controls.

**/

#include "HiiDemo.h"

EFI_GUID mHiiDemoFormSetGuid = HII_DEMO_FORMSET_GUID;
HII_DEMO_CONFIGURATION mHiiDemoConfiguration;

//
// HII vendor device path template
//
HII_VENDOR_DEVICE_PATH  mHiiDemoHiiVendorDevicePath = {
  {
    {
      HARDWARE_DEVICE_PATH,
      HW_VENDOR_DP,
      {
        (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
        (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
      }
    },
    HII_DEMO_FORMSET_GUID
  },
  {
    END_DEVICE_PATH_TYPE,
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
    {
      (UINT8) (END_DEVICE_PATH_LENGTH),
      (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
    }
  }
};

//
// Global variables
//
EFI_HANDLE mDriverHandle = NULL;

//
// Forward declarations
//
EFI_STATUS
EFIAPI
ExtractConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Request,
  OUT EFI_STRING                             *Progress,
  OUT EFI_STRING                             *Results
  );

EFI_STATUS
EFIAPI
RouteConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Configuration,
  OUT EFI_STRING                             *Progress
  );

EFI_STATUS
EFIAPI
Callback (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  EFI_BROWSER_ACTION                     Action,
  IN  EFI_QUESTION_ID                        QuestionId,
  IN  UINT8                                  Type,
  IN  EFI_IFR_TYPE_VALUE                     *Value,
  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
  );

//
// HII Config Access Protocol instance
//
EFI_HII_CONFIG_ACCESS_PROTOCOL mHiiConfigAccess = {
  ExtractConfig,
  RouteConfig,
  Callback
};

/**
  This function allows a caller to extract the current configuration for one
  or more named elements from the target driver.

  @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
  @param Request         A null-terminated Unicode string in <ConfigRequest> format.
  @param Progress        On return, points to a character in the Request string.
  @param Results         A null-terminated Unicode string in <ConfigAltResp> format.

  @retval EFI_SUCCESS             The Results is filled with the requested values.
  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the results.
  @retval EFI_INVALID_PARAMETER   Request is illegal syntax, or unknown name.
  @retval EFI_NOT_FOUND           Routing data doesn't match any storage in this driver.

**/
EFI_STATUS
EFIAPI
ExtractConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Request,
  OUT EFI_STRING                             *Progress,
  OUT EFI_STRING                             *Results
  )
{
  EFI_STATUS                       Status;
  UINTN                            BufferSize;
  EFI_HII_CONFIG_ROUTING_PROTOCOL  *HiiConfigRouting;
  EFI_STRING                       ConfigRequest;
  EFI_STRING                       ConfigResp;

  if (Progress == NULL || Results == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  *Progress = Request;
  *Results = NULL;
  
  //
  // Get the HII Config Routing Protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiHiiConfigRoutingProtocolGuid,
                  NULL,
                  (VOID **) &HiiConfigRouting
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
  //
  BufferSize = sizeof (HII_DEMO_CONFIGURATION);
  ConfigRequest = Request;
  if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
    //
    // Request has no request element, construct full request string.
    //
    ConfigRequest = NULL;
  }

  Status = HiiConfigRouting->BlockToConfig (
                               HiiConfigRouting,
                               ConfigRequest,
                               (UINT8 *) &mHiiDemoConfiguration,
                               BufferSize,
                               &ConfigResp,
                               Progress
                               );
  if (!EFI_ERROR (Status)) {
    *Results = ConfigResp;
  }

  return Status;
}

/**
  This function processes the results of changes in configuration.

  @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
  @param Configuration   A null-terminated Unicode string in <ConfigResp> format.
  @param Progress        A pointer to a string filled in with the offset of the most
                         recent '&' before the first failing name/value pair.

  @retval EFI_SUCCESS             The Results is processed successfully.
  @retval EFI_INVALID_PARAMETER   Configuration is NULL.
  @retval EFI_NOT_FOUND           Routing data doesn't match any storage in this driver.

**/
EFI_STATUS
EFIAPI
RouteConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Configuration,
  OUT EFI_STRING                             *Progress
  )
{
  EFI_STATUS                       Status;
  UINTN                            BufferSize;
  EFI_HII_CONFIG_ROUTING_PROTOCOL  *HiiConfigRouting;

  if (Configuration == NULL || Progress == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  *Progress = Configuration;
  
  //
  // Get the HII Config Routing Protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiHiiConfigRoutingProtocolGuid,
                  NULL,
                  (VOID **) &HiiConfigRouting
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Convert <ConfigResp> to buffer data by helper function ConfigToBlock()
  //
  BufferSize = sizeof (HII_DEMO_CONFIGURATION);
  Status = HiiConfigRouting->ConfigToBlock (
                               HiiConfigRouting,
                               Configuration,
                               (UINT8 *) &mHiiDemoConfiguration,
                               &BufferSize,
                               Progress
                               );

  return Status;
}

/**
  This function processes the results of changes in configuration.

  @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
  @param Action          Specifies the type of action taken by the browser.
  @param QuestionId      A unique value which is sent to the original exporting driver.
  @param Type            The type of value for the question.
  @param Value           A pointer to the data being sent to the original exporting driver.
  @param ActionRequest   On return, points to the action requested by the callback function.

  @retval EFI_SUCCESS             The callback successfully handled the action.
  @retval EFI_OUT_OF_RESOURCES    Not enough storage is available to hold the variable and its data.
  @retval EFI_DEVICE_ERROR        The variable could not be saved.
  @retval EFI_UNSUPPORTED         The specified Action is not supported by the callback.

**/
EFI_STATUS
EFIAPI
Callback (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  EFI_BROWSER_ACTION                     Action,
  IN  EFI_QUESTION_ID                        QuestionId,
  IN  UINT8                                  Type,
  IN  EFI_IFR_TYPE_VALUE                     *Value,
  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
  )
{
  if (ActionRequest != NULL) {
    *ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
  }
  
  switch (Action) {
    case EFI_BROWSER_ACTION_FORM_OPEN:
      Print (L"Form opened\n");
      break;
      
    case EFI_BROWSER_ACTION_FORM_CLOSE:
      Print (L"Form closed\n");
      break;
      
    case EFI_BROWSER_ACTION_CHANGING:
      Print (L"Question %d changing\n", QuestionId);
      break;
      
    case EFI_BROWSER_ACTION_CHANGED:
      Print (L"Question %d changed\n", QuestionId);
      break;
      
    default:
      return EFI_UNSUPPORTED;
  }
  
  return EFI_SUCCESS;
}

/**
  Display the HII form using Form Browser.

  @param[in] HiiHandle    The HII handle for the form.

  @retval EFI_SUCCESS     The form was displayed successfully.
  @retval other           Error occurred while displaying the form.

**/
EFI_STATUS
DisplayHiiForm (
  IN EFI_HII_HANDLE    HiiHandle
  )
{
  EFI_STATUS                    Status;
  EFI_FORM_BROWSER2_PROTOCOL    *FormBrowser2;
  EFI_BROWSER_ACTION_REQUEST    ActionRequest;

  //
  // Locate Form Browser2 Protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiFormBrowser2ProtocolGuid,
                  NULL,
                  (VOID **) &FormBrowser2
                  );
  if (EFI_ERROR (Status)) {
    Print (L"Error: Cannot locate Form Browser2 Protocol - %r\n", Status);
    return Status;
  }

  Print (L"Form Browser2 Protocol located successfully\n");

  Print (L"About to call SendForm...\n");
  Print (L"FormSet GUID: %g\n", &mHiiDemoFormSetGuid);
  Print (L"Form ID: 0x%x (using 0 for auto-select)\n", 0);

  //
  // Send Form to the Form Browser
  //
  ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
  Status = FormBrowser2->SendForm (
                           FormBrowser2,
                           &HiiHandle,
                           1,
                           &mHiiDemoFormSetGuid,
                           0,
                           NULL,
                           &ActionRequest
                           );

  Print (L"SendForm returned with status: %r\n", Status);

  if (EFI_ERROR (Status)) {
    Print (L"Error: SendForm failed - %r\n", Status);
    return Status;
  }

  Print (L"Form displayed and closed successfully\n");
  Print (L"Action Request: %d\n", ActionRequest);

  return EFI_SUCCESS;
}

/**
  The user Entry Point for Application. The user code starts with this function
  as the real entry point for the application.

  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
  @param[in] SystemTable    A pointer to the EFI System Table.

  @retval EFI_SUCCESS       The entry point is executed successfully.
  @retval other             Some error occurs when executing this entry point.

**/
EFI_STATUS
EFIAPI
HiiDemoMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                     Status;
  EFI_HII_HANDLE                 HiiHandle;
  EFI_INPUT_KEY                  Key;

  Print (L"HII Demo Application Started\n");
  Print (L"Step 1: Initializing...\n");

  //
  // Initialize configuration data with default values
  //
  ZeroMem (&mHiiDemoConfiguration, sizeof (HII_DEMO_CONFIGURATION));
  mHiiDemoConfiguration.CheckboxValue = 1;
  mHiiDemoConfiguration.NumericValue = 50;
  mHiiDemoConfiguration.OneOfValue = 1;
  StrCpyS (mHiiDemoConfiguration.StringValue, 20, L"Default Text");

  Print (L"Step 2: Installing Device Path and Config Access Protocol...\n");

  //
  // Install Device Path Protocol and Config Access protocol to driver handle
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &mDriverHandle,
                  &gEfiDevicePathProtocolGuid,
                  &mHiiDemoHiiVendorDevicePath,
                  &gEfiHiiConfigAccessProtocolGuid,
                  &mHiiConfigAccess,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    Print (L"Failed to install protocols - %r\n", Status);
    goto Exit;
  }
  
  Print (L"Protocols installed successfully\n");

  Print (L"Step 3: Installing HII packages...\n");

  //
  // Install HII packages
  //
  HiiHandle = HiiAddPackages (
                &mHiiDemoFormSetGuid,
                mDriverHandle,
                HiiDemoStrings,
                HiiDemoVfrBin,
                NULL
                );
  if (HiiHandle == NULL) {
    Print (L"Failed to install HII packages\n");
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }

  Print (L"Step 4: HII packages installed successfully\n");
  Print (L"Step 5: HII Handle = 0x%p\n", HiiHandle);

  Print (L"This is a basic HII demo showing that HII packages can be installed.\n");
  Print (L"The form contains various HII controls like:\n");
  Print (L"- Text controls\n");
  Print (L"- Checkbox\n");
  Print (L"- Numeric input\n");
  Print (L"- OneOf (dropdown)\n");
  Print (L"- String input\n");
  Print (L"- Ordered list\n");
  Print (L"- Password input\n");
  Print (L"- Conditional controls\n");

  Print (L"Step 6: Verifying HII Database...\n");
  
  //
  // Try to locate HII Database Protocol to verify our package is registered
  //
  EFI_HII_DATABASE_PROTOCOL *HiiDatabase;
  Status = gBS->LocateProtocol (
                  &gEfiHiiDatabaseProtocolGuid,
                  NULL,
                  (VOID **) &HiiDatabase
                  );
  if (EFI_ERROR (Status)) {
    Print (L"Error: Cannot locate HII Database Protocol - %r\n", Status);
  } 
  // else {
  //   Print (L"HII Database Protocol located successfully\n");
    
  //   //
  //   // Try to get package list from our handle
  //   //
  //   UINTN BufferSize = 0;
  //   EFI_HII_PACKAGE_LIST_HEADER *PackageList = NULL;
    
  //   Status = HiiDatabase->ExportPackageLists (
  //                           HiiDatabase,
  //                           HiiHandle,
  //                           &BufferSize,
  //                           PackageList
  //                           );
  //   if (Status == EFI_BUFFER_TOO_SMALL) {
  //     Print (L"Package list size: %d bytes\n", BufferSize);
  //     Print (L"HII package verification successful\n");
  //   } else {
  //     Print (L"Package list export failed - %r\n", Status);
  //   }
  // }

  //
  // Displaying HII form
  //
  Print (L"Step 7: Displaying HII form...\n");
  
  //
  // Now try to display the form with proper Config Access Protocol
  //
  Status = DisplayHiiForm (HiiHandle);
  if (EFI_ERROR (Status)) {
    Print (L"Error: Failed to display HII form - %r\n", Status);
  } else {
    Print (L"HII form displayed successfully\n");
  }
  
  Print (L"Step 8: Removing HII packages...\n");
  HiiRemovePackages (HiiHandle);
  
  //
  // Uninstall protocols
  //
  if (mDriverHandle != NULL) {
    gBS->UninstallMultipleProtocolInterfaces (
           mDriverHandle,
           &gEfiDevicePathProtocolGuid,
           &mHiiDemoHiiVendorDevicePath,
           &gEfiHiiConfigAccessProtocolGuid,
           &mHiiConfigAccess,
           NULL
           );
  }

Exit:
  Print (L"HII Demo Application Completed\n");
  Print (L"Press any key to exit...\n");
  
  //
  // Wait for user input
  //
  while (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {
    gBS->Stall (10000); // Wait 10ms
  }
  
  return EFI_SUCCESS;
}